/*
 * Copyright (c) 2010-2020 Belledonne Communications SARL.
 *
 * This file is part of linphone-android
 * (see https://www.linphone.org).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.newlink.building.activities.main

import android.app.Activity
import android.content.ComponentCallbacks2
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnAttach
import androidx.databinding.DataBindingUtil
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.FragmentContainerView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.window.layout.FoldingFeature
import com.alibaba.android.arouter.facade.annotation.Route
import com.google.android.material.snackbar.Snackbar
import com.newlink.building.common_base.constant.Base_RouterPath
import com.newlink.building.NL_App.Companion.coreContext
import com.newlink.building.NL_App.Companion.corePreferences
import com.newlink.building.R
import com.newlink.building.activities.Module_Phone_GenericActivity
import com.newlink.building.activities.Module_Phone_SnackBarActivity
import com.newlink.building.activities.main.viewmodels.Module_Phone_CallOverlayViewModel
import com.newlink.building.activities.main.viewmodels.Module_Phone_ListTopBarViewModel
import com.newlink.building.activities.main.viewmodels.Module_Phone_SharedMainViewModel
import com.newlink.building.activities.navigateToDialer
import com.newlink.building.activities.navigateToSettings
import com.newlink.building.compatibility.Module_Phone_Compatibility
import com.newlink.building.compatibility.ContactsUpdatedListenerStub
import com.newlink.building.core.Module_Phone_CorePreferences
import com.newlink.building.databinding.MainActivityLinphoneBinding
import com.newlink.building.utils.Module_Phone_AppUtils
import com.newlink.building.utils.Module_Phone_Event
import com.newlink.building.utils.Module_Phone_FileUtils
//import com.vdroid.dooraccess.module_linphone.utils.GlideApp
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import kotlin.math.abs
import kotlinx.coroutines.*
import org.linphone.core.tools.Log

@Route(path = Base_RouterPath.PATH_LINPHONE_MAIN)
class Module_Phone_LinphoneMainActivity :
    Module_Phone_GenericActivity(),
    Module_Phone_SnackBarActivity,
    NavController.OnDestinationChangedListener {
    private lateinit var binding: MainActivityLinphoneBinding
    private lateinit var sharedViewModel: Module_Phone_SharedMainViewModel
    private lateinit var callOverlayViewModel: Module_Phone_CallOverlayViewModel
    private lateinit var listTopBarViewModel: Module_Phone_ListTopBarViewModel

    private val listener = object : ContactsUpdatedListenerStub() {
        override fun onContactsUpdated() {
            Log.i("[Main Activity] Contact(s) updated, update shortcuts")
            if (corePreferences.contactsShortcuts) {
                Module_Phone_Compatibility.createShortcutsToContacts(this@Module_Phone_LinphoneMainActivity)
            } else if (corePreferences.chatRoomShortcuts) {
                Module_Phone_Compatibility.createShortcutsToChatRooms(this@Module_Phone_LinphoneMainActivity)
            }
        }
    }

    private lateinit var tabsFragment: FragmentContainerView
//    private lateinit var statusFragment: FragmentContainerView

    private var overlayX = 0f
    private var overlayY = 0f
    private var initPosX = 0f
    private var initPosY = 0f
    private var overlay: View? = null

    private val componentCallbacks = object : ComponentCallbacks2 {
        override fun onConfigurationChanged(newConfig: Configuration) {}

        override fun onLowMemory() {
            Log.w("[Main Activity] onLowMemory !")
        }

        override fun onTrimMemory(level: Int) {
            Log.w("[Main Activity] onTrimMemory called with level $level !")
//            GlideApp.get(this@LinphoneMainActivity).clearMemory()
        }
    }

    override fun onLayoutChanges(foldingFeature: FoldingFeature?) {
        sharedViewModel.layoutChangedEvent.value = Module_Phone_Event(true)
    }

    private var tabsFragmentVisible1 = true
    private var tabsFragmentVisible2 = true

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val splashScreen = installSplashScreen()

        binding = DataBindingUtil.setContentView(this, R.layout.main_activity_linphone)
        binding.lifecycleOwner = this

        sharedViewModel = ViewModelProvider(this)[Module_Phone_SharedMainViewModel::class.java]
        binding.viewModel = sharedViewModel

        callOverlayViewModel = ViewModelProvider(this)[Module_Phone_CallOverlayViewModel::class.java]
        binding.callOverlayViewModel = callOverlayViewModel
        listTopBarViewModel = ViewModelProvider(this)[Module_Phone_ListTopBarViewModel::class.java]
        binding.listTopBarViewModel = listTopBarViewModel

        sharedViewModel.toggleDrawerEvent.observe(
            this
        ) {
//            it.consume {
//                if (binding.sideMenu.isDrawerOpen(Gravity.LEFT)) {
//                    binding.sideMenu.closeDrawer(binding.sideMenuContent, true)
//                } else {
//                    binding.sideMenu.openDrawer(binding.sideMenuContent, true)
//                }
//            }
        }

        sharedViewModel.jumpToDialHomePage.observe(
            this
        ) {
            it.consume {
                navigateToDialer(null)
            }
        }

        sharedViewModel.jumpToSettingsPage.observe(
            this
        ) {
            it.consume {
                navigateToSettings(null)
            }
        }

        sharedViewModel.exitApplication.observe(
            this
        ) {
            it.consume {
                this@Module_Phone_LinphoneMainActivity.finish()
            }
        }

        coreContext.callErrorMessageResourceId.observe(
            this
        ) {
            it.consume { message ->
                showSnackBar(message)
            }
        }
        if (coreContext.core.accountList.isEmpty()) {
//            if (corePreferences.firstStart) {
//            startActivity(Intent(this, LoginActivity::class.java))
//            }
        }

        tabsFragment = findViewById(R.id.tabs_fragment)
//        statusFragment = findViewById(R.id.status_fragment)
//        tabsFragment.menu.forEach {
//            tabsFragment.findViewById<View>(it.itemId).setOnLongClickListener {
//                true
//            }
//        }
        /**
         * 禁止DrawerLayout侧滑
         */
        findViewById<DrawerLayout>(R.id.side_menu).setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)

        val navHostFragment =
            supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
// //        tabsFragment.setupWithNavController(navHostFragment.navController)
//        NavigationUI.setupWithNavController(tabsFragment, navHostFragment.navController, false)
//
        navHostFragment.navController.addOnDestinationChangedListener(
            listener = { _, destination, _ ->

                sharedViewModel.hideNavigationBar.value = false
                when (destination.id) {
                    R.id.dialerFragment -> {
                        //                        controller.navigate(R.id.dialerFragment)
                        // 解决联系人编辑状态,popBackStack后底部导航消失问题
                        if (listTopBarViewModel.isEditionEnabled.value == true) {
                            listTopBarViewModel.isEditionEnabled.value = false
                        }
                    }
                }
            }
        )

        binding.root.doOnAttach {
            Log.i("[Main Activity] Report UI has been fully drawn (TTFD)")
            try {
                reportFullyDrawn()
            } catch (se: SecurityException) {
                Log.e("[Main Activity] Security exception when doing reportFullyDrawn(): $se")
            }
        }
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)

        if (intent != null) handleIntentParams(intent)
    }

    override fun onResume() {
        super.onResume()
        coreContext.contactsManager.addListener(listener)
    }

    override fun onPause() {
        coreContext.contactsManager.removeListener(listener)
        super.onPause()
    }

    override fun showSnackBar(resourceId: Int) {
        Snackbar.make(findViewById(R.id.coordinator), resourceId, Snackbar.LENGTH_LONG).show()
    }

    override fun showSnackBar(resourceId: Int, action: Int, listener: () -> Unit) {
        Snackbar
            .make(findViewById(R.id.coordinator), resourceId, Snackbar.LENGTH_LONG)
            .setAction(action) {
                Log.i("[Snack Bar] Action listener triggered")
                listener()
            }
            .show()
    }

    override fun showSnackBar(message: String) {
        Snackbar.make(findViewById(R.id.coordinator), message, Snackbar.LENGTH_LONG).show()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)

        registerComponentCallbacks(componentCallbacks)
        findNavController(R.id.nav_host_fragment).addOnDestinationChangedListener(this)

        binding.rootCoordinatorLayout.viewTreeObserver.addOnGlobalLayoutListener {
            val portraitOrientation =
                resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
            val keyboardVisible = ViewCompat.getRootWindowInsets(binding.rootCoordinatorLayout)
                ?.isVisible(WindowInsetsCompat.Type.ime()) == true
            Log.d("[Tabs Fragment] Keyboard is ${if (keyboardVisible) "visible" else "invisible"}")
            tabsFragmentVisible2 = !portraitOrientation || !keyboardVisible
            updateTabsFragmentVisibility()
        }

        initOverlay()

        if (intent != null) handleIntentParams(intent)
    }

    override fun onDestroy() {
        findNavController(R.id.nav_host_fragment).removeOnDestinationChangedListener(this)
        unregisterComponentCallbacks(componentCallbacks)
        super.onDestroy()
    }

    override fun onDestinationChanged(
        controller: NavController,
        destination: NavDestination,
        arguments: Bundle?
    ) {
        hideKeyboard()
//        if (statusFragment.visibility == View.GONE) {
//            statusFragment.visibility = View.VISIBLE
//        }

        tabsFragmentVisible1 = when (destination.id) {
//            R.id.masterCallLogsFragment, R.id.masterContactsFragment, R.id.dialerFragment, R.id.masterChatRoomsFragment ->
            R.id.dialerFragment ->
                true

            else -> false
        }
        updateTabsFragmentVisibility()
    }

    fun hideKeyboard() {
        currentFocus?.hideKeyboard()
    }

    private fun updateTabsFragmentVisibility() {
//        tabsFragment.visibility = if (tabsFragmentVisible1 && tabsFragmentVisible2) View.VISIBLE else View.GONE
    }

    private fun View.hideKeyboard() {
        val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(windowToken, 0)
    }

    private fun handleIntentParams(intent: Intent) {
        when (intent.action) {
            Intent.ACTION_SEND, Intent.ACTION_SENDTO -> {
                if (intent.type == "text/plain") {
                    handleSendText(intent)
                } else {
                    lifecycleScope.launch {
                        handleSendFile(intent)
                    }
                }
            }

            Intent.ACTION_SEND_MULTIPLE -> {
                lifecycleScope.launch {
                    handleSendMultipleFiles(intent)
                }
            }

            Intent.ACTION_VIEW -> {
                val uri = intent.data
                if (intent.type == Module_Phone_AppUtils.getString(R.string.linphone_address_mime_type)) {
                    if (uri != null) {
                        val contactId = coreContext.contactsManager.getAndroidContactIdFromUri(uri)
                        if (contactId != null) {
                            Log.i("[Main Activity] Found contact URI parameter in intent: $uri")
//                            navigateToContact(contactId)
                        }
                    }
                } else {
                    if (uri != null) {
                        handleTelOrSipUri(uri)
                    }
                }
            }

            Intent.ACTION_DIAL, Intent.ACTION_CALL -> {
                val uri = intent.data
                if (uri != null) {
                    handleTelOrSipUri(uri)
                }
            }

            Intent.ACTION_VIEW_LOCUS -> {
                if (corePreferences.disableChat) return
                val locus = Module_Phone_Compatibility.extractLocusIdFromIntent(intent)
                if (locus != null) {
                    Log.i("[Main Activity] Found chat room locus intent extra: $locus")
                    handleLocusOrShortcut(locus)
                }
            }

            "SOFTPHONE_EXIT_APPLICATION" -> {
                finish()
            }

            else -> {
                when {
                    intent.hasExtra("ContactId") -> {
                        val id = intent.getStringExtra("ContactId")
                        Log.i("[Main Activity] Found contact ID in extras: $id")
//                        navigateToContact(id)
                    }

                    intent.hasExtra("Chat") -> {
                        if (corePreferences.disableChat) return

                        if (intent.hasExtra("RemoteSipUri") && intent.hasExtra("LocalSipUri")) {
                            val peerAddress = intent.getStringExtra("RemoteSipUri")
                            val localAddress = intent.getStringExtra("LocalSipUri")
                            Log.i("[Main Activity] Found chat room intent extra: local SIP URI=[$localAddress], peer SIP URI=[$peerAddress]")
//                            navigateToChatRoom(localAddress, peerAddress)
                        } else {
                            Log.i("[Main Activity] Found chat intent extra, go to chat rooms list")
//                            navigateToChatRooms()
                        }
                    }

                    intent.hasExtra("Dialer") -> {
                        Log.i("[Main Activity] Found dialer intent extra, go to dialer")
                        val args = Bundle()
                        args.putBoolean("Transfer", intent.getBooleanExtra("Transfer", false))
                        navigateToDialer(args)
                    }
                }
            }
        }

        // Prevent this intent to be processed again
        intent.action = null
        intent.data = null
        intent.extras?.clear()
    }

    private fun handleTelOrSipUri(uri: Uri) {
        Log.i("[Main Activity] Found uri: $uri to call")
        val stringUri = uri.toString()
        var addressToCall: String = stringUri

        when {
            addressToCall.startsWith("tel:") -> {
                Log.i("[Main Activity] Removing tel: prefix")
                addressToCall = addressToCall.substring("tel:".length)
            }

            addressToCall.startsWith("linphone:") -> {
                Log.i("[Main Activity] Removing linphone: prefix")
                addressToCall = addressToCall.substring("linphone:".length)
            }

            addressToCall.startsWith("sip-linphone:") -> {
                Log.i("[Main Activity] Removing linphone: sip-linphone")
                addressToCall = addressToCall.substring("sip-linphone:".length)
            }
        }

        val address = coreContext.core.interpretUrl(addressToCall)
        if (address != null) {
            addressToCall = address.asStringUriOnly()
        }

        Log.i("[Main Activity] Starting dialer with pre-filled URI $addressToCall")
        val args = Bundle()
        args.putString("URI", addressToCall)
        navigateToDialer(args)
    }

    private fun handleSendText(intent: Intent) {
        if (corePreferences.disableChat) return

        intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            sharedViewModel.textToShare.value = it
        }

        handleSendChatRoom(intent)
    }

    private suspend fun handleSendFile(intent: Intent) {
        if (corePreferences.disableChat) return

        Log.i("[Main Activity] Found single file to share with type ${intent.type}")

        (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
            val list = arrayListOf<String>()
            coroutineScope {
                val deferred = async {
                    Module_Phone_FileUtils.getFilePath(this@Module_Phone_LinphoneMainActivity, it)
                }
                val path = deferred.await()
                if (path != null) {
                    list.add(path)
                    Log.i("[Main Activity] Found single file to share: $path")
                }
            }
            sharedViewModel.filesToShare.value = list
        }

        // Check that the current fragment hasn't already handled the event on filesToShare
        // If it has, don't go further.
        // For example this may happen when picking a GIF from the keyboard while inside a chat room
        if (!sharedViewModel.filesToShare.value.isNullOrEmpty()) {
            handleSendChatRoom(intent)
        }
    }

    private suspend fun handleSendMultipleFiles(intent: Intent) {
        if (corePreferences.disableChat) return

        intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
            val list = arrayListOf<String>()
            coroutineScope {
                val deferred = arrayListOf<Deferred<String?>>()
                for (parcelable in it) {
                    val uri = parcelable as Uri
                    deferred.add(async { Module_Phone_FileUtils.getFilePath(this@Module_Phone_LinphoneMainActivity, uri) })
                }
                val paths = deferred.awaitAll()
                for (path in paths) {
                    Log.i("[Main Activity] Found file to share: $path")
                    if (path != null) list.add(path)
                }
            }
            sharedViewModel.filesToShare.value = list
        }

        handleSendChatRoom(intent)
    }

    private fun handleSendChatRoom(intent: Intent) {
        if (corePreferences.disableChat) return

        val uri = intent.data
        if (uri != null) {
            Log.i("[Main Activity] Found uri: $uri to send a message to")
            val stringUri = uri.toString()
            var addressToIM: String = stringUri
            try {
                addressToIM = URLDecoder.decode(stringUri, "UTF-8")
            } catch (e: UnsupportedEncodingException) {
                Log.e("[Main Activity] UnsupportedEncodingException: $e")
            }

            when {
                addressToIM.startsWith("sms:") ->
                    addressToIM = addressToIM.substring("sms:".length)

                addressToIM.startsWith("smsto:") ->
                    addressToIM = addressToIM.substring("smsto:".length)

                addressToIM.startsWith("mms:") ->
                    addressToIM = addressToIM.substring("mms:".length)

                addressToIM.startsWith("mmsto:") ->
                    addressToIM = addressToIM.substring("mmsto:".length)
            }

            val localAddress =
                coreContext.core.defaultAccount?.params?.identityAddress?.asStringUriOnly()
            val peerAddress = coreContext.core.interpretUrl(addressToIM)?.asStringUriOnly()
            Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses")
//            navigateToChatRoom(localAddress, peerAddress)
        } else {
            val shortcutId =
                intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
            if (shortcutId != null) {
                Log.i("[Main Activity] Found shortcut ID: $shortcutId")
                handleLocusOrShortcut(shortcutId)
            } else {
                Log.i("[Main Activity] Going into chat rooms list")
//                navigateToChatRooms()
            }
        }
    }

    private fun handleLocusOrShortcut(id: String) {
        val split = id.split("~")
        if (split.size == 2) {
            val localAddress = split[0]
            val peerAddress = split[1]
            Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses, computed from shortcut/locus id")
//            navigateToChatRoom(localAddress, peerAddress)
        } else {
            Log.e("[Main Activity] Failed to parse shortcut/locus id: $id, going to chat rooms list")
//            navigateToChatRooms()
        }
    }

    private fun initOverlay() {
        overlay = binding.root.findViewById(R.id.call_overlay)
        val callOverlay = overlay
        callOverlay ?: return

        callOverlay.setOnTouchListener { view, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    overlayX = view.x - event.rawX
                    overlayY = view.y - event.rawY
                    initPosX = view.x
                    initPosY = view.y
                }

                MotionEvent.ACTION_MOVE -> {
                    view.animate()
                        .x(event.rawX + overlayX)
                        .y(event.rawY + overlayY)
                        .setDuration(0)
                        .start()
                }

                MotionEvent.ACTION_UP -> {
                    if (abs(initPosX - view.x) < Module_Phone_CorePreferences.OVERLAY_CLICK_SENSITIVITY &&
                        abs(initPosY - view.y) < Module_Phone_CorePreferences.OVERLAY_CLICK_SENSITIVITY
                    ) {
                        view.performClick()
                    }
                }

                else -> return@setOnTouchListener false
            }
            true
        }

        callOverlay.setOnClickListener {
            coreContext.onCallOverlayClick()
        }
    }
}
